const path = require('path');
const _saas = require('./saas');
const _config = require('./config/config');
const _adapter = require('./config/adapter');
const _crontab = require('./config/crontab');
const _controller = require('./core/controller');
const _service = require('./core/service');
const _model = require('./core/model');
const _domain = require('./core/domain');
const NacosConfigClient = require('nacos').NacosConfigClient;
const grpcServerLoader = require('./microservice/grpcServerLoader');
const grpcClientLoader = require('./microservice/grpcClientLoader');
const Package = require('../package');
const fs = require('fs');
const schedule = require('node-schedule');
const hook = require('./core/hook');

global.saas = _saas;
saas.config = _config;
saas.adapter = _adapter;
saas.crontab = _crontab;

saas.controller = _controller;
saas.svc = _service;

saas.Controller = _controller;
saas.Service = _service;
saas.Model = _model;
saas.Domain = _domain;

module.exports = class Application {

  middlewarePath;
  pluginPath;
  fastifyOptionsPath;

  constructor(configPath = null, adapterPath = null) {
    this.loadData(configPath, adapterPath);

    saas.hook = new hook();
  }

  setProperty(propertyName, propertyValue) {
    this[propertyName] = propertyValue;
  }

  loadData(configPath, adapterPath) {
    // configPath = configPath || path.resolve(process.cwd() + '/config/', 'config.js');
    // adapterPath = adapterPath || path.resolve(process.cwd() + '/config/', 'adapter.js');
    configPath = configPath || './config/config.js';
    adapterPath = adapterPath || './config/adapter.js';
    const adapter = require(adapterPath);
    const config = require(configPath);
    saas.adapter = Object.assign(saas.adapter, adapter);
    saas.config = Object.assign(saas.config, config);
    if (!saas.isEmpty(saas.config.nacos)) {
      saas.configClient = new NacosConfigClient({
        serverAddr: saas.config.nacos.serverList,
      });
    }
  }

  loadMiddleware() {
    if (saas.isEmpty(this.middlewarePath)) {
      return
    }
    const middleware = require(this.middlewarePath);
    for (const middle of middleware) {
      if (saas.isEmpty(middle.path)) {
        saas.app.use(middle.handle)
      } else {
        saas.app.use(middle.path, middle.handle)
      }
    }
  }

  loadPlugin() {
    if (saas.isEmpty(this.pluginPath)) {
      return
    }
    const plugins = require(this.pluginPath);
    for (const plugin of plugins) {
      saas.app.register(plugin.handle, plugin.options)
    }
  }

  async regesiterPlugin() {
    if (saas.adapter.cache.type === 'redis') {
      saas.app.register(require('fastify-redis'), saas.adapter.cache.redis);
    }
    saas.cache = require('./extend/saas-cache');

    //start grpcServer
    if (saas.config.microservice) {
      await grpcServerLoader();
      await grpcClientLoader();
    }
    //start rabbitmq
    if (!saas.isEmpty(saas.config.rabbitmq.url) && saas.config.rabbitmq.url.length > 0) {
      const RabbitMQ = require('./extend/saas-rabbitmq');
      saas.mq = new RabbitMQ();
    }

    if (saas.adapter.mysql) {
      saas.app.register(require('./extend/saas-mysql'), saas.adapter.mysql);
    }

    //load crontab
    const crontabPath = path.resolve(saas.ROOT_PATH + '/config/crontab');
    if (fs.existsSync(crontabPath + '.js')) {
      const crontab = require(crontabPath);
      saas.crontab = Object.assign(saas.crontab, crontab);
    }
    for (const item of saas.crontab) {
      schedule.scheduleJob(item.cron, () => {
        saas.service(item.handle);
      })
    }

    this.loadPlugin()
  }

  async start() {
    await saas.hook.run('beforeCreate');
    const coreOptions = require('./config/fastifyOptions');
    if (!saas.isEmpty(this.fastifyOptionsPath)) {
      const fastifyOptions = require(this.fastifyOptionsPath);
      Object.assign(coreOptions, fastifyOptions);
    }

    const fastify = require('fastify')(coreOptions);
    saas.app = fastify;

    await saas.hook.run('created');

    this.loadMiddleware();

    await this.regesiterPlugin();

    saas.app.register(require('./extend/saas-view-nunjucks'), { viewPath: process.cwd() + '/views' });

    saas.app.register(require('fastify-multipart'), saas.config.fileUpload);

    saas.app.register(require('./core/router'));

    await saas.hook.run('beforeStart');

    saas.app.listen(saas.config.port, '0.0.0.0', async (err, address) => {
      if (err) throw err;
      console.log(`
   #####                                  #####
  #                                          ##
 ##         ####      ####       ####        ##     ####
  ##        #   ##   ##  ##    ##   #        ##   ###  #
   ####         ##        ##   ##            ##   ##
     ###    ######    ######   #####         ##    ####
       ##  ##   ##   ##   ##       ##        ##       ###
       ##  ##   ##   #    ##        ##       ##        ##
 #######    ######   #######   ######   ######    ###### `)
      console.log('[saas-js] current version: ' + Package.version);
      console.log('[saas-js] server started!');
      await saas.hook.run('started');
    })
  }

};

module.exports.saas = global.saas;